-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2
--
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.
local log = require 'mc.logging'
local cjson = require 'cjson'
local context = require 'mc.context'
local redfish_utils = require 'redfish_utils'
local vos = require 'utils.vos'

local UMS_PATH = '/bmc/kepler/Managers/1/Ums/Ums_SP_00'
local UMS_INTERFACE = 'bmc.kepler.Managers.Ums'

-- 加密情况下的 osinstall 文件存放路径
local SP_OSINSTALL_PARA_PATH = "/data/opt/bmc/sp/sposinstallpara"
-- ums 目录下的 osinstall 文件名
local UMS_OSINSTALL_FILE_NAME = "osinstall.json"

local UMS_TRANS_FILE_ID_SP_OSINSTALL = 3

local sp_job_type = {
    ['SPOSInstall'] = 1,
    ['SPDiagnose'] = 1,
    ['SPFWUpdate'] = 1,
}

local m = {}

-- 获取 UMS 对象
local function get_ums_object()
    local is_ok, ums_obj

    is_ok, ums_obj = pcall(mdb.get_object, bus, UMS_PATH, UMS_INTERFACE)
    if not is_ok then
        log:error('Get ums object failed, err(%s)', ums_obj)
        return nil
    end

    return ums_obj
end

function m.get_spfwupdate_messages(message_id, message_args)
    local message_array = cjson.json_object_new_array()

    if not message_id or #message_id == 0 then
        return message_array
    end

    local error_message = redfish_utils.create_error_message(message_id, nil, message_args)
    message_array[#message_array + 1] = error_message
    return message_array
end

-- 返回的Templates的对象，只需要添加一次，后续具体模板信息放入该对象的 Contents 中
local function get_templates_first(templates)
    -- 当前仅有 OSInstall 类型模板，预期 templates 长度为 1
    if #templates > 0 then
        return
    end

    templates.Templates = cjson.json_object_new_array()
    local obj = cjson.json_object_new_object()
    obj.Type = 'SPOSInstall'
    obj.Contents = cjson.json_object_new_array()    -- Contents 内为具体模板内容
    templates.Templates[#templates.Templates + 1] = obj
end

-- 将 spcfg 中的模板内容放入到结果中
local function get_os_template(ret_templates, templates)
    for _, template in pairs(templates) do
        ret_templates.Templates[1].Contents[#ret_templates.Templates[1].Contents + 1] = template
    end
end

local function get_templates(spcfg_json_obj)
    local templates = cjson.json_object_new_object()
    for _, os_array in pairs(spcfg_json_obj.OS) do
        for _, os in pairs(os_array) do
            if os.Templates then
                get_templates_first(templates)
                get_os_template(templates, os.Templates)
            end
        end
    end

    return templates
end

local function get_common_rsp(interface_name, uri, content)
    local ret = cjson.json_object_new_object()

    if not content or #content == 0 then
        return ret
    end

    local is_ok, json_obj = pcall(cjson.json_object_ordered_decode, content)
    if not is_ok then
        log:error("Decode content failed, uri:%s, err(%s)", uri, json_obj)
        return ret
    end

    is_ok, ret = pcall(get_templates, json_obj)
    if not is_ok then
        log:error("get_templates failed, err(%s)", ret)
    end

    return ret
end

function m.is_deviceinfo_valid(content)
    if not content then
        error(custom_messages.UMSIsExclusivelyUsed())
    end
    return true
end

function m.get_cfg_rsp(uri, content)
    return get_common_rsp("SPCfg", uri, content)
end

function m.check_osinstall_file_exist()
    -- 先检查 pme 目录下的 sposinstallpara 文件是否存在
    local res = vos.get_file_accessible(SP_OSINSTALL_PARA_PATH)
    if res then
        return true
    end

    local is_ok, ums_obj, rsp

    ums_obj = get_ums_object()
    if not ums_obj then
        log:error('Get ums obj failed.')
        return false
    end

    -- 再检查 ums 下 osinstall.json 文件是否存在
    is_ok, rsp = ums_obj.pcall:CheckFileExist(context.new(), UMS_TRANS_FILE_ID_SP_OSINSTALL, UMS_OSINSTALL_FILE_NAME)
    if not is_ok then
        log:error('Check file exist failed, file(%s), err(%s)', UMS_OSINSTALL_FILE_NAME, rsp)
        return false
    end

    return rsp.Result
end

-- 按照入参，组装成a{ss}格式的资源树参数
function m.get_update_job_params(job_id, job_state, start_time, json_body)
    local params = {}
    local path = "/bmc/kepler/JobService/Jobs/"..job_id
    local ok, obj = pcall(mdb.get_object, bus, path, 'bmc.kepler.JobService.Job')
    if not ok or not obj or not sp_job_type[obj.JobType] then
        error(base_messages.PropertyValueNotInList(job_id, 'JobId'))
    end

    if job_state and job_state ~= "" and job_state ~= cjson.null then
        params.JobState = job_state
    end
    if start_time and start_time ~= cjson.null then
        params.InitialStartTime = start_time
    end
    if json_body and json_body ~= "" and json_body ~= cjson.null then
        params.JsonBody = json_body
    end
    return params
end

function m.get_create_job_params(job_type, start_time, json_body)
    local params = {}
    if job_type and job_type ~= "" then
        params.JobType = job_type
    end
    if start_time and start_time ~= "" then
        params.InitialStartTime = start_time
    end
    if json_body and json_body ~= "" then
        params.JsonBody = json_body
    end
    return params
end

function m.get_delete_job_path(job_ids)
    local paths = cjson.json_object_new_array()
    local path
    for _, id in ipairs(job_ids) do
        path = "/bmc/kepler/JobService/Jobs/"..id
        local ok, obj = pcall(mdb.get_object, bus, path, 'bmc.kepler.JobService.Job')
        if ok and obj and sp_job_type[obj.JobType] then
            paths[#paths + 1] = path
        end
    end
    return paths
end

local function get_record_obj(job)
    record = cjson.json_object_new_object()
    record.Id = job.Id
    record.EndTime = job.EndTime
    record.JobState = job.JobState
    record.JobType = job.JobType
    record.InitialStartTime = job.InitialStartTime
    record.JsonBody = job.JsonBody
    record.CreateTime = job.CreateTime
    record.Abortable = job.Abortable
    return record
end

local function select_by_type_and_id(ret, data, jobType, jobId)
    for _, job in pairs(data) do
        if job.JobType == jobType and job.Id == jobId and job.Origin == 'WEB' then
            ret[#ret + 1] = get_record_obj(job)
        end
    end
end

local function select_by_type(ret, data, jobType)
    for _, job in pairs(data) do
        if job.JobType == jobType and job.Origin == 'WEB' then
            ret[#ret + 1] = get_record_obj(job)
        end
    end
end

local function select_by_id(ret, data, jobId)
    for _, job in pairs(data) do
        if job.Id == jobId and job.Origin == 'WEB' and sp_job_type[job.JobType] then
            ret[#ret + 1] = get_record_obj(job)
        end
    end
end

local function select_all(ret, data)
    for _, job in pairs(data) do
        if sp_job_type[job.JobType] and job.Origin == 'WEB' then
            ret[#ret + 1] = get_record_obj(job)
        end
    end
end

local function is_common_user(privileges)
    for _, value in ipairs(privileges) do
        if value ~= 'ReadOnly' and value ~= 'ConfigureSelf' then
            return false
        end
    end

    return true
end

-- 按照Qurey的需要，格式化返回的值
function m.format_get_jobs(jobType, jobId, data, privileges)
    local ret = cjson.json_object_new_array()

    -- job任务信息不允许低权限用户查询，只有拥有非ReadOnly和ConfigureSelf权限的用户可以查询
    if is_common_user(privileges) then
        return ret
    end

    local select_Id = jobId and jobId ~= ""
    local select_type = jobType and jobType ~= ""
    if select_type and not sp_job_type[jobType] then
        return ret
    end

    if select_type and select_Id then
        select_by_type_and_id(ret, data, jobType, jobId)
    elseif select_type then
        select_by_type(ret, data, jobType)
    elseif select_Id then
        select_by_id(ret, data, jobId)
    else
        select_all(ret, data)
    end
    return ret
end

function m.print_id(data)
    log:info('sp work webrest delete: %s', data)
    return data
end

function m.error_id(id)
    error(base_messages.ActionNotSupported(id))
    return "/bmc/kepler/JobService/Jobs"
end

function m.get_template_enabled(data)
    local ret = cjson.json_object_new_array()
    local info
    for _, obj in pairs(data and data.Templates or {}) do
        info = cjson.json_object_new_object()
        info.Type = obj.Type
        info.Supported = true
        ret[#ret + 1] = info
    end
    return ret
end

return m
